package top.someapp.applib.kits;

import java.math.BigDecimal;
import top.someapp.applib.cast.Castor;
import top.someapp.applib.cast.DefaultCastor;

/**
 * @author zwz
 * Created on 2019-10-06
 */
public class Numbers {

    public static final long JS_MIN_SAFE_INT = -9007199254740991L;
    public static final long JS_MAX_SAFE_INT = 9007199254740991L;

    private Numbers() {
        // no need instance.
    }

    // cast
    public static Number parseNumber(String s) {
        return new BigDecimal(s);
    }

    public static Number parseNumber(String s, Number defaultValue) {
        Number n = defaultValue;
        try {
            n = new BigDecimal(s);
        } catch (Exception e) {
            // ignored
        }
        return n;
    }

    public static float toFloat(Object val, float defaultValue) {
        if (val == null) {
            return defaultValue;
        }
        if (val instanceof Float) {
            return (float) val;
        }
        float rtn = defaultValue;
        try {
            rtn = DefaultCastor.castTo(float.class, val, defaultValue, new Castor<Float>() {
                @Override
                public Float cast(Object from, Float failValue) throws ClassCastException {
                    if (from instanceof Number) {
                        return ((Number) from).floatValue();
                    }
                    if (from instanceof String) {
                        return new BigDecimal((String) from).floatValue();
                    }
                    return failValue;
                }
            });
        } catch (Exception ignored) {
        }
        return rtn;
    }

    public static double toDouble(Object val, double defaultValue) {
        if (val == null) {
            return defaultValue;
        }
        if (val instanceof Double) {
            return (double) val;
        }

        double rtn = defaultValue;
        try {
            rtn = DefaultCastor.castTo(double.class, val, defaultValue, new Castor<Double>() {
                @Override
                public Double cast(Object from, Double failValue) throws ClassCastException {
                    if (from instanceof Number) {
                        return ((Number) from).doubleValue();
                    }
                    if (from instanceof String) {
                        return new BigDecimal((String) from).doubleValue();
                    }
                    return failValue;
                }
            });
        } catch (Exception ignored) {
        }
        return rtn;
    }

    public static int toInt(Object val, int defaultValue) {
        if (val == null) {
            return defaultValue;
        } else if (val instanceof Integer) {
            return (int) val;
        } else if (val instanceof Number) {
            return ((Number) val).intValue();
        } else {
            int rtn = defaultValue;
            try {
                rtn = DefaultCastor.castTo(int.class, val, defaultValue);
            } catch (Exception ignored) {
            }
            return rtn;
        }
    }

    public static long toLong(Object val, long defaultValue) {
        if (val == null) {
            return defaultValue;
        }
        if (val instanceof Long) {
            return (long) val;
        }
        long rtn = defaultValue;
        try {
            rtn = DefaultCastor.castTo(long.class, val, defaultValue, new Castor<Long>() {
                @Override
                public Long cast(Object from, Long failValue) throws ClassCastException {
                    if (from instanceof Number) {
                        return ((Number) from).longValue();
                    }
                    if (from instanceof String) {
                        return new BigDecimal((String) from).longValue();
                    }
                    return failValue;
                }
            });
        } catch (Exception ignored) {
        }
        return rtn;
    }
    // ~cast

    // compare
    public static boolean isValueEQ(Number n1, Number n2) {
        if (n1 == null || n2 == null) {
            return false;
        }
        long l1 = n1.longValue();
        long l2 = n2.longValue();
        if (l1 - l2 != 0) {
            return false;
        }
        BigDecimal v1 = BigDecimal.valueOf(n1.doubleValue() - l1);
        BigDecimal v2 = BigDecimal.valueOf(n2.doubleValue() - l2);
        return v1.subtract(v2).doubleValue() == 0.0;
    }

    public static boolean isLongLevelEQ(Number n1, Number n2) {
        if (n1 == null || n2 == null) {
            return false;
        }
        return n1.longValue() - n2.longValue() == 0;
    }

    public static boolean isFloatLevelEQ(Number n1, Number n2) {
        if (n1 == null || n2 == null) {
            return false;
        }
        if (!isLongLevelEQ(n1, n2)) {
            return false;
        }
        float f1 = n1.floatValue() - n1.longValue();
        float f2 = n2.floatValue() - n2.longValue();
        return f1 - f2 == 0;
    }

    /**
     * 检查value是否在JavaScript能够准确表示的整数范围内.<br>
     * JavaScript 能够准确表示的整数范围在 -2^53 到 2^53之间（不含两个端点），超过这个范围，无法精确表示这个值。
     *
     * @param value 待检查的值
     * @return true：value是一个JavaScript 安全整数；false：value超出JavaScript 安全整数范围
     */
    public static boolean isJSSafe(long value) {
        return value >= JS_MIN_SAFE_INT && value <= JS_MAX_SAFE_INT;
    }
    // ~compare

    // to String
    public static String fillZero(int i, int length) {
        return Strings.formatInt(i, length);
    }
    // ~to String
}
